<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>实时波形显示器</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.7.2/socket.io.min.js"></script>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: linear-gradient(135deg, #1a2a6c, #2c3e50);
            color: #ecf0f1;
            overflow: hidden;
            height: 100vh;
        }
        
        #container {
            display: flex;
            flex-direction: column;
            height: 100vh;
            padding: 15px;
            gap: 15px;
        }
        
        .header {
            background: rgba(30, 30, 46, 0.8);
            border-radius: 12px;
            padding: 15px 20px;
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4);
            backdrop-filter: blur(10px);
            display: flex;
            justify-content: space-between;
            align-items: center;
            flex-wrap: wrap;
            gap: 15px;
        }
        
        .title {
            font-size: 1.8rem;
            font-weight: 600;
            background: linear-gradient(90deg, #4facfe, #00f2fe);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
        }
        
        .controls {
            display: flex;
            gap: 12px;
            flex-wrap: wrap;
        }
        
        .btn {
            background: rgba(74, 74, 110, 0.7);
            color: #e0e0ff;
            border: none;
            padding: 10px 18px;
            border-radius: 8px;
            cursor: pointer;
            transition: all 0.3s ease;
            font-weight: 500;
            box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
        }
        
        .btn:hover {
            background: rgba(90, 90, 142, 0.9);
            transform: translateY(-2px);
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
        }
        
        .btn:active {
            transform: translateY(0);
        }
        
        .legend {
            display: flex;
            gap: 20px;
            margin-left: 15px;
        }
        
        .channel {
            display: flex;
            align-items: center;
            gap: 8px;
            font-size: 0.95rem;
        }
        
        .color-box {
            width: 18px;
            height: 18px;
            border-radius: 4px;
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
        }
        
        #channel1-color { background: linear-gradient(90deg, #ff6b6b, #ff8e8e); }
        #channel2-color { background: linear-gradient(90deg, #4ecdc4, #88f3e8); }
        
        .canvas-container {
            flex: 1;
            background: rgba(25, 25, 40, 0.7);
            border-radius: 12px;
            overflow: hidden;
            position: relative;
            box-shadow: inset 0 0 15px rgba(0, 0, 0, 0.5);
        }
        
        canvas {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
        }
        
        .info-bar {
            position: absolute;
            bottom: 15px;
            left: 15px;
            background: rgba(37, 37, 54, 0.8);
            color: #e0e0ff;
            padding: 8px 15px;
            border-radius: 8px;
            font-size: 0.9rem;
            display: flex;
            gap: 20px;
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
        }
        
        .loading {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            color: #e0e0ff;
            font-size: 1.2rem;
            background: rgba(37, 37, 54, 0.8);
            padding: 15px 25px;
            border-radius: 8px;
            display: none;
        }
        
        .status {
            display: flex;
            align-items: center;
            gap: 10px;
        }
        
        .status-indicator {
            width: 12px;
            height: 12px;
            border-radius: 50%;
            background-color: #4caf50;
            box-shadow: 0 0 8px #4caf50;
        }
        
        @media (max-width: 768px) {
            .header {
                flex-direction: column;
                align-items: flex-start;
            }
            
            .controls {
                width: 100%;
                justify-content: center;
            }
            
            .legend {
                margin-left: 0;
                justify-content: center;
                width: 100%;
            }
        }
    </style>
</head>
<body>
    <div id="container">
        <div class="header">
            <div class="title">双通道实时波形显示器</div>
            <div class="controls">
                <button id="zoom-in" class="btn">放大 (Z)</button>
                <button id="zoom-out" class="btn">缩小 (X)</button>
                <button id="reset" class="btn">重置视图 (R)</button>
                <div class="legend">
                    <div class="channel">
                        <div id="channel1-color" class="color-box"></div>
                        <span>通道 1</span>
                    </div>
                    <div class="channel">
                        <div id="channel2-color" class="color-box"></div>
                        <span>通道 2</span>
                    </div>
                </div>
            </div>
        </div>
        
        <div class="canvas-container">
            <canvas id="waveform"></canvas>
            <div class="info-bar">
                <div class="status">
                    <div class="status-indicator"></div>
                    <span>实时数据中...</span>
                </div>
                <div id="time-display">时间范围: 0.00s - 600.00s</div>
                <div id="resolution-display">分辨率: 1920×1080</div>
                <div id="data-rate">数据率: 0点/秒</div>
            </div>
            <div id="loading" class="loading">加载数据中...</div>
        </div>
    </div>

    <script>
        // 获取DOM元素
        const canvas = document.getElementById('waveform');
        const ctx = canvas.getContext('2d');
        const zoomInBtn = document.getElementById('zoom-in');
        const zoomOutBtn = document.getElementById('zoom-out');
        const resetBtn = document.getElementById('reset');
        const timeDisplay = document.getElementById('time-display');
        const resolutionDisplay = document.getElementById('resolution-display');
        const dataRateDisplay = document.getElementById('data-rate');
        const loadingIndicator = document.getElementById('loading');
        
        // 状态变量
        let data = [];
        let timeRange = [0, 0]; // [start, end] in seconds
        let isDragging = false;
        let lastX = 0;
        let scaleFactor = 1.0;
        let animationFrameId = null;
        let lastUpdateTime = 0;
        let dataPointsPerSecond = 0;
        let dataCounter = 0;
        let dataTimer = 0;
        const BUFFER_DURATION = 600; // 10分钟
        
        // 初始化Canvas大小
        function initCanvas() {
            const container = document.querySelector('.canvas-container');
            canvas.width = container.clientWidth;
            canvas.height = container.clientHeight;
            resolutionDisplay.textContent = `分辨率: ${canvas.width}×${canvas.height}`;
        }
        
        // 连接WebSocket
        const socket = io();
        
        // 处理初始数据
        socket.on('initial_data', (dataObj) => {
            data = dataObj.data;
            timeRange = dataObj.time_range;
            updateTimeDisplay();
            drawWaveform();
            hideLoading();
        });
        
        // 处理实时数据点
        socket.on('new_data_point', (point) => {
            // 添加新数据点
            data.push([point.ch1, point.ch2]);
            
            // 移除旧数据点以保持缓冲区大小
            const maxPoints = BUFFER_DURATION * 1000;
            if (data.length > maxPoints) {
                data.shift();
                timeRange[0] = timeRange[1] - (data.length / 1000);
            }
            
            // 更新时间范围
            timeRange[1] = timeRange[0] + (data.length / 1000);
            
            // 更新数据率计数器
            dataCounter++;
        });
        
        // 更新时间显示
        function updateTimeDisplay() {
            const start = timeRange[0].toFixed(2);
            const end = timeRange[1].toFixed(2);
            timeDisplay.textContent = `时间范围: ${start}s - ${end}s`;
        }
        
        // 显示加载指示器
        function showLoading() {
            loadingIndicator.style.display = 'block';
        }
        
        // 隐藏加载指示器
        function hideLoading() {
            loadingIndicator.style.display = 'none';
        }
        
        // 绘制波形
        function drawWaveform() {
            if (data.length === 0) return;
            
            // 清除画布
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            
            // 绘制网格背景
            drawGrid();
            
            // 计算时间比例
            const totalTime = timeRange[1] - timeRange[0];
            const timePerPixel = totalTime / canvas.width;
            
            // 绘制通道1
            drawChannel(data, 0, '#ff6b6b', '通道1');
            
            // 绘制通道2
            drawChannel(data, 1, '#4ecdc4', '通道2');
        }
        
        // 绘制单个通道
        function drawChannel(data, channelIndex, color, label) {
            if (data.length === 0) return;
            
            const totalTime = timeRange[1] - timeRange[0];
            const timePerPixel = totalTime / canvas.width;
            const pointsPerPixel = Math.max(1, data.length / canvas.width);
            
            ctx.beginPath();
            ctx.strokeStyle = color;
            ctx.lineWidth = 2;
            ctx.lineJoin = 'round';
            
            for (let i = 0; i < canvas.width; i++) {
                // 计算数据索引
                const dataIndex = Math.floor(i * pointsPerPixel);
                if (dataIndex >= data.length) break;
                
                // 获取数据值
                const value = data[dataIndex][channelIndex];
                
                // 计算坐标
                const x = i;
                const y = canvas.height - (value / 65535) * canvas.height;
                
                if (i === 0) {
                    ctx.moveTo(x, y);
                } else {
                    ctx.lineTo(x, y);
                }
            }
            
            ctx.stroke();
            
            // 添加通道标签
            ctx.fillStyle = color;
            ctx.font = '14px "Segoe UI", sans-serif';
            ctx.fillText(label, 10, 30 + channelIndex * 25);
        }
        
        // 绘制网格
        function drawGrid() {
            const gridSize = 80; // 像素
            const minorGridSize = 20;
            
            // 主网格
            ctx.strokeStyle = 'rgba(100, 100, 140, 0.3)';
            ctx.lineWidth = 1;
            
            for (let x = 0; x <= canvas.width; x += gridSize) {
                ctx.beginPath();
                ctx.moveTo(x, 0);
                ctx.lineTo(x, canvas.height);
                ctx.stroke();
            }
            
            for (let y = 0; y <= canvas.height; y += gridSize) {
                ctx.beginPath();
                ctx.moveTo(0, y);
                ctx.lineTo(canvas.width, y);
                ctx.stroke();
            }
            
            // 次网格
            ctx.strokeStyle = 'rgba(80, 80, 120, 0.2)';
            for (let x = 0; x <= canvas.width; x += minorGridSize) {
                ctx.beginPath();
                ctx.moveTo(x, 0);
                ctx.lineTo(x, canvas.height);
                ctx.stroke();
            }
            
            for (let y = 0; y <= canvas.height; y += minorGridSize) {
                ctx.beginPath();
                ctx.moveTo(0, y);
                ctx.lineTo(canvas.width, y);
                ctx.stroke();
            }
        }
        
        // 缩放视图
        function zoom(factor) {
            const centerTime = (timeRange[0] + timeRange[1]) / 2;
            const currentRange = timeRange[1] - timeRange[0];
            const newRange = currentRange * factor;
            
            // 限制缩放范围
            if (newRange < 0.1 || newRange > 600) return;
            
            timeRange[0] = centerTime - newRange / 2;
            timeRange[1] = centerTime + newRange / 2;
            
            // 确保时间范围在有效范围内
            const currentTime = timeRange[1];
            const minTime = currentTime - BUFFER_DURATION;
            
            if (timeRange[0] < minTime) {
                timeRange[1] -= (timeRange[0] - minTime);
                timeRange[0] = minTime;
            }
            
            updateTimeDisplay();
        }
        
        // 平移视图
        function pan(offset) {
            const timePerPixel = (timeRange[1] - timeRange[0]) / canvas.width;
            const timeOffset = offset * timePerPixel;
            
            timeRange[0] -= timeOffset;
            timeRange[1] -= timeOffset;
            
            // 确保时间范围在有效范围内
            const currentTime = timeRange[1];
            const minTime = currentTime - BUFFER_DURATION;
            
            if (timeRange[0] < minTime) {
                timeRange[1] -= (timeRange[0] - minTime);
                timeRange[0] = minTime;
            }
            
            updateTimeDisplay();
        }
        
        // 重置视图
        function resetView() {
            const currentTime = timeRange[1];
            timeRange = [currentTime - BUFFER_DURATION, currentTime];
            updateTimeDisplay();
        }
        
        // 更新数据率显示
        function updateDataRate() {
            const now = Date.now();
            if (now - dataTimer >= 1000) {
                dataPointsPerSecond = dataCounter;
                dataCounter = 0;
                dataTimer = now;
                dataRateDisplay.textContent = `数据率: ${dataPointsPerSecond}点/秒`;
            }
        }
        
        // 动画循环
        function animate() {
            drawWaveform();
            updateDataRate();
            animationFrameId = requestAnimationFrame(animate);
        }
        
        // 事件监听器
        zoomInBtn.addEventListener('click', () => zoom(0.5));
        zoomOutBtn.addEventListener('click', () => zoom(2));
        resetBtn.addEventListener('click', resetView);
        
        // 键盘快捷键
        document.addEventListener('keydown', (e) => {
            if (e.key === 'z' || e.key === 'Z') zoom(0.5);
            if (e.key === 'x' || e.key === 'X') zoom(2);
            if (e.key === 'r' || e.key === 'R') resetView();
        });
        
        // 鼠标拖动事件
        canvas.addEventListener('mousedown', (e) => {
            isDragging = true;
            lastX = e.clientX;
            canvas.style.cursor = 'grabbing';
        });
        
        canvas.addEventListener('mousemove', (e) => {
            if (isDragging) {
                const offsetX = e.clientX - lastX;
                lastX = e.clientX;
                pan(offsetX);
            }
        });
        
        canvas.addEventListener('mouseup', () => {
            isDragging = false;
            canvas.style.cursor = 'default';
        });
        
        canvas.addEventListener('mouseleave', () => {
            isDragging = false;
            canvas.style.cursor = 'default';
        });
        
        // 鼠标滚轮缩放
        canvas.addEventListener('wheel', (e) => {
            e.preventDefault();
            const zoomFactor = e.deltaY > 0 ? 1.25 : 0.8;
            zoom(zoomFactor);
        });
        
        // 窗口大小变化时调整Canvas
        window.addEventListener('resize', () => {
            initCanvas();
            drawWaveform();
        });
        
        // 初始化
        showLoading();
        initCanvas();
        animate();
    </script>
</body>
</html>